#pragma once
#include "../common/net.hpp"
#include "../common/dispatcher.hpp"
#include "requestor.hpp"
#include "rpc_caller.hpp"
#include "rpc_register.hpp"
#include "rpc_topic.hpp"
#include <chrono>

namespace MyRpc
{
    namespace client
    {
        // 注册服务
        class ProviderClient
        {
        public:
            using ptr = std::shared_ptr<ProviderClient>;
            ProviderClient(const std::string& ip, int port)
                :_requestor(std::make_shared<Requestor>())
                ,_provider(std::make_shared<Provider>(_requestor))    
            {

                // 把这回调函数注册给dispatcher模块
                auto cb = std::bind(&MyRpc::client::Requestor::onResponse, _requestor.get(), std::placeholders::_1, std::placeholders::_2);
                MyRpc::disp->Register<MyRpc::BaseMessage>(MyRpc::MType::RSP_SERVICE, cb);
                _client = ClientFactory::create(ip, port);
                // 给客户端注册收到信息的回调函数
                _client->setMessageCallBack(std::bind(&MyRpc::Dispatcher::OnMessage, disp.get(), std::placeholders::_1, std::placeholders::_2));
                // 连接服务器
                _client->connect();
            }
            // 向服务端进行注册方法主机
            bool registerMethod(const std::string& method, const Addr& host)
            {
                return _provider->registerMethod(_client->connection(), method, host);
            }
        private:
            Requestor::ptr _requestor;  // 进行发送请求
            Provider::ptr _provider;    // 用来注册服务
            BaseClient::ptr _client;    // 保存客户端信息
        };

        // 发现服务 服务上下线的功能
        class DiscoveryClient
        {
        public:
            using ptr = std::shared_ptr<DiscoveryClient>;
            DiscoveryClient(const std::string& ip, int port, const Discovery::OffLineCallBack& offlinecb)
                :_requestor(std::make_shared<Requestor>())
                ,_discovery(std::make_shared<Discovery>(_requestor, offlinecb))    
            {
                _client = ClientFactory::create(ip, port);
                // 把这回调函数注册给dispatcher模块
                auto cb = std::bind(&MyRpc::client::Requestor::onResponse, _requestor.get(), std::placeholders::_1, std::placeholders::_2);
                disp->Register<BaseMessage>(MyRpc::MType::RSP_SERVICE, cb);
                // 给dispathcer模块注册服务上下线的回调函数
                auto service_cb = std::bind(&Discovery::onServiceRequest, _discovery.get(), std::placeholders::_1, std::placeholders::_2);
                disp->Register<ServiceRequest>(MType::REQ_SERVICE, service_cb);
                _client->setMessageCallBack(std::bind(&MyRpc::Dispatcher::OnMessage, disp.get(), std::placeholders::_1, std::placeholders::_2));
                // 连接服务器
                _client->connect();
                LOG(DEBUGLEVEL, "服务发现客户端已经连接");
            }
            bool ServiceDiscovery(const std::string& method, Addr& addr)
            {
                return _discovery->ServiceDiscovery(_client->connection(), method, addr);
            }
        private:
            Requestor::ptr _requestor;
            Discovery::ptr _discovery;
            BaseClient::ptr _client;
        };

        // 进行请求发送的类
        class RpcClient
        {
        public:
            using ptr = std::shared_ptr<RpcClient>;
            RpcClient(bool enabledis, const std::string& ip, int port)
                :_enabledis(enabledis)
                ,_requestor(std::make_shared<Requestor>())
                ,_caller(std::make_shared<RpcCaller>(_requestor))
            {
                // 把这回调函数注册给dispatcher模块
                auto cb = std::bind(&MyRpc::client::Requestor::onResponse, _requestor.get(), std::placeholders::_1, std::placeholders::_2);
                disp->Register<MyRpc::BaseMessage>(MyRpc::MType::RSP_RPC, cb);
                if(_enabledis) // 启用服务发现模块
                {
                    LOG(DEBUGLEVEL, "启用服务发现模块");
                    auto offline_cb = std::bind(&RpcClient::delClient, this, std::placeholders::_1);
                    _discovery_client = std::make_shared<DiscoveryClient>(ip, port, offline_cb);
                }
                else
                {
                    // 未启用服务发现模块 这里的ip 和 port直接就是服务提供者的地址
                    _client = ClientFactory::create(ip, port);
                    _client->setMessageCallBack(std::bind(&MyRpc::Dispatcher::OnMessage, disp.get(), std::placeholders::_1, std::placeholders::_2));
                    // 连接服务器
                    _client->connect();
                }
            }
            bool call(const std::string& method, const Json::Value& param, Json::Value& result)
            {
                // LOG(DEBUGLEVEL, "进入到发送请求中!");
                BaseClient::ptr client = getClient(method);  // 这一步就是在进行服务发现
                if(client.get() == nullptr)
                {
                    return false;
                }
                // 进行发送请求
                return _caller->call(client->connection(), method, param, result);
            }
            bool call(const std::string& method, const Json::Value& param, RpcCaller::AsyncResponse& result)
            {
                BaseClient::ptr client = getClient(method);
                if(client.get() == nullptr)
                {
                    return false;
                }
                // 进行发送请求
                return _caller->call(client->connection(), method, param, result);
            }
            bool call(const std::string& method, const Json::Value& param, const RpcCaller::ResponseCallBack& cb)
            {
                BaseClient::ptr client = getClient(method);
                if(client.get() == nullptr)
                {
                    return false;
                }
                // 进行发送请求
                return _caller->call(client->connection(), method, param, cb);
            }
        private:
            struct AddrHash
            {
                // 用一个仿函数解决pair对象无法自己生成hash值的问题
                size_t operator()(const Addr& addr) const
                {
                    std::string ret = addr.first + std::to_string(addr.second);
                    // hash计算的返回格式
                    return std::hash<std::string>{}(ret);
                }
            };
            BaseClient::ptr newClient(const Addr& addr)
            {
                BaseClient::ptr client;
                {
                    std::lock_guard<std::mutex> lock(_mtx);
                    // 没有找打这个服务的连接 创建一个这个服务的连接
                    client = ClientFactory::create(addr.first, addr.second);
                    client->setMessageCallBack(std::bind(&MyRpc::Dispatcher::OnMessage, disp.get(), std::placeholders::_1, std::placeholders::_2));
                    // 连接服务器
                    client->connect();
                    // 然后再添加进去
                }
                addClient(addr, client);
                return client;
            }

            BaseClient::ptr getClient(const std::string& method)
            {
                BaseClient::ptr client;
                // LOG(DEBUGLEVEL, "进入到寻找服务的连接中");
                if(_enabledis)
                {
                    Addr addr;
                    // 服务发现开启 先获取服务的主机
                    bool flag = _discovery_client->ServiceDiscovery(method, addr);
                    if(flag == false)
                    {
                        // 没有找到这个服务的主机地址
                        LOG(DEBUGLEVEL, "没有发现这个服务的主机地址 这个 %s 服务没有上线", method.c_str());
                        return BaseClient::ptr();
                    }
                    // 找到了这个服务的主机地址
                    client = selectClient(addr);
                    if (client.get() == nullptr)
                    {
                        client = newClient(addr);
                        LOG(DEBUGLEVEL, "新的服务已经被添加");
                    }
                }
                else
                {
                    // 服务发现没有开启
                    client = _client;
                }
                return client;
            }

            BaseClient::ptr selectClient(const Addr& addr)
            {
                std::lock_guard<std::mutex> lock(_mtx);
                auto it = _client_hosts.find(addr);
                if(it == _client_hosts.end())
                {
                    // 没有找到这个服务连接
                    return BaseClient::ptr();
                }
                // 找到了
                return it->second;
            }
            BaseClient::ptr addClient(const Addr& addr, const BaseClient::ptr& client)
            {
                std::unique_lock<std::mutex> lock(_mtx);
                _client_hosts.insert(std::make_pair(addr, client));
            }
            
            void delClient(const Addr& addr)
            {
                std::unique_lock<std::mutex> lock(_mtx);
                auto it = _client_hosts.find(addr);
                if(it == _client_hosts.end())
                {
                    return;
                }
                _client_hosts.erase(addr);
            }
        private:
            std::mutex _mtx;
            bool _enabledis;
            DiscoveryClient::ptr _discovery_client;  // 用来发现服务的主机
            Requestor::ptr _requestor;
            RpcCaller::ptr _caller;
            BaseClient::ptr _client;     // 未启用发现服务 直接就是服务提供者的地址
            std::unordered_map<Addr, BaseClient::ptr, AddrHash> _client_hosts;  // 用于保存主机地址和他对应的连接
        };

        class TopicClinent
        {
        public:
            using ptr = std::shared_ptr<TopicClinent>;
            TopicClinent(const std::string& ip, int port)
                :_requestor(std::make_shared<Requestor>())
                ,_topics(std::make_shared<TopicManage>(_requestor))
            {
                // 把收到应答的回调函数注册给dispatcher模块
                auto cb = std::bind(&MyRpc::client::Requestor::onResponse, _requestor.get(), std::placeholders::_1, std::placeholders::_2);
                disp->Register<MyRpc::BaseMessage>(MyRpc::MType::RSP_TOPIC, cb);

                auto it = std::bind(&TopicManage::onPublish, _topics.get(), std::placeholders::_1, std::placeholders::_2);
                disp->Register<TopicRequest>(MType::REQ_TOPIC, it);
                // 未启用服务发现模块 这里的ip 和 port直接就是服务提供者的地址
                _client = ClientFactory::create(ip, port);
                _client->setMessageCallBack(std::bind(&MyRpc::Dispatcher::OnMessage, disp.get(), std::placeholders::_1, std::placeholders::_2));
                // 连接服务器
                _client->connect();
            }
            
            bool create(const std::string& key) 
            {
                return _topics->create(_client->connection(), key);
            }
            bool remove(const std::string& key)
            {
                return _topics->remove(_client->connection(), key);
            }
            bool subscribe(const std::string& key, const TopicManage::SubscribeCB& cb)
            {
                return _topics->subscribe(_client->connection(), key, cb);
            }
            bool cancel(const std::string& key)
            {
                return _topics->cancel(_client->connection(), key);
            }
            bool publish(const std::string& key, const std::string& msg)
            {
                return _topics->publish(_client->connection(), key, msg);
            }
            void shutDown()
            {
                _client->shutdown();
            }
        private:
            std::mutex _mtx;
            Requestor::ptr _requestor;
            TopicManage::ptr _topics;  // 用来发现服务的主机
            BaseClient::ptr _client;
        };
    }
}